﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;


namespace Xant.Querier.Core
{
    /// <summary>
    /// 算式因子，表达两个因子之间的运算关系
    /// </summary>
    /// <remarks>
    /// 算式因子中的两个因子可以任何类型的因子，当为算式因子时，构成嵌套，可用于表达复杂的算式(例如：(Items.Qty+Item.Adjust)*Items.UnitPrice)
    /// </remarks>
    public class Formular: Factor
    {
        public Factor Left { get; private set; }
        public  MathOperator Operator { get; private set; }
        public Factor Right { get; private set; }
        /*废除，已经支持自动分析多级算式中各子算式的运算优先级
        public bool Unitaried { get; set; }*/

        public Formular(Factor left, MathOperator operatr, Factor right)
        {
            if (left == null)
            {
                throw new ArgumentNullException("left", "left argument can not be null.");
            }
            if (right == null && !operatr.IsUnary())
            {
                throw new ArgumentNullException("right", "right argument can not be null.");
            }
            //为避免编译查询时过于复杂，限定当Left和Right因子都为字段或含有字段时，两者的Path必须一致。
            string pathLeft = left.ExtractPrefixPath();
            string pathRight = right == null ? null : right.ExtractPrefixPath();
            if (pathLeft != null && pathRight != null && !pathLeft.Equals(pathRight))
            {
                throw new ArgumentException("两个因子同为字段或含有字段的算式因子时，两者的字段路径必须一致。");
            }
            this.Left = left;
            this.Operator = operatr;
            this.Right = right;
        }

        /*废除，已经支持自动分析多级算式中各子算式的运算优先级
        public Formular Unitary()
        {
            this.Unitaried = true;
            return this;
        }*/

        public bool HasFieldInside(bool recursive)
        {
            bool has = Left is Field || Right is Field;
            if (!has && recursive)
            {
                has = Left is Formular && (Left as Formular).HasFieldInside(true)
                || Right is Formular && (Right as Formular).HasFieldInside(true);
            }
            return has;
        }

        public ReadOnlyCollection<Field> AllFields()
        {
            var fields = new List<Field>();
            Left.PickField(fields);
            Right.PickField(fields);
            return fields.AsReadOnly();
        }

        public Field[] Fields()
        {
            var b1 = Left is Field;
            var b2 = Right is Field;
            var count = Convert.ToInt16(b1) + Convert.ToInt16(b2);
            var fields = new Field[count];
            if (b1)
            {
                fields[0] = Left as Field;
            }
            if (b2)
            {
                fields[Convert.ToInt16(b1)] = Right as Field;
            }
            return fields;
        }

        /// <summary>
        /// 左因子(子算式)运算符优先级是否比此算式本身的运算符优先级低
        /// </summary>
        public bool LeftFactorOperatorIsLower
        {
            get
            {
                if (!(Left is Formular))
                    return false;
                return (Left as Formular).Operator.IsLowerPriorityThan(this.Operator);
            }
        }

        /// <summary>
        /// 右因子(子算式)运算符优先级是否比此算式本身的运算符优先级低
        /// </summary>
        public bool RightFactorOperatorIsLower
        {
            get
            {
                if (!(Right is Formular))
                    return false;
                return (Right as Formular).Operator.IsLowerPriorityThan(this.Operator);
            }
        }

        public override string ToString()
        {
            var left = "{0}";
            var right = "{2}";
            if (this.LeftFactorOperatorIsLower)//如果左因子为算式，且算式运算符优先级较低，则用括号包围
            {
                left = "(" + left + ")";
            }
            if (this.RightFactorOperatorIsLower)//如果右因子为算式，且算式运算符优先级较低，则用括号包围
            {
                right = "(" + right + ")";
            }
            string format = left + "{1}" + right;
            return string.Format(format, this.Left, this.Operator.GetOperatorString(), this.Right);
            //return this.Operator.GetOperatorString();
        }

    }
}
